Skip to content

account: use dwshell with more than one DWService account - #9

Merged
ale-rinaldi merged 3 commits into
mainfrom
feat/multi-account
Sep 5, 2026
Merged

account: use dwshell with more than one DWService account#9
ale-rinaldi merged 3 commits into
mainfrom
feat/multi-account

Conversation

@ale-rinaldi

Copy link
Copy Markdown
Member

Implements docs/superpowers/specs/2026-09-05-multi-account-design.md.

Log in twice with different emails and dwshell keeps both; choose between them with --account or DWSHELL_ACCOUNT. The email is the account's identity, so logging in again with a known one refreshes it exactly as before.

$ dwshell account list
[email protected]  (default)
[email protected]

$ dwshell list                              # the default account
$ dwshell list --account [email protected]   # the other one

The two requirements, as tests rather than intentions

Invisible until wanted. With a single account there is no default to think about: Select uses the lone account whether or not it is marked default, no output changes, and the word appears only in account list. TestSelectWithOneAccountAndNoDefault pins it.

An existing configuration keeps working. The flat shape is migrated in memory on load and written in the new shape only when something saves for a reason of its own — a read-only command must not rewrite the user's file behind their back. TestLoadDoesNotRewriteTheFile pins that, and it was confirmed against a real configuration:

file keys
before session, trustedDevice, user
after dwshell list session, trustedDevice, user — untouched
after a command that saves accounts, default

The migration test builds its fixture from generated keys, not invented ones: the signing key is parsed on load, so a made-up JWK would fail for reasons unrelated to migration.

Decisions worth flagging

  • Removing the default promotes the survivor when exactly one remains, and otherwise leaves it unset, refusing later commands with an explanation. Guessing would silently point the next command at the wrong account.
  • login refuses --account — which account it touches is decided by the email — and deliberately does not select one, since "several accounts, no default" is a state logging in resolves.
  • @ is not a selector: dwshell alice@myserver already means the remote OS user.
  • Flags follow their subcommand (dwshell list --account …). This is pre-existing for every flag including --config; the shell shortcut still accepts them first (dwshell --account … myserver). Documented in the README.

Verification

Live, with two real accounts that see the same machines from opposite sides — the second owns what the first sees as shares — so a wrong selection is obvious rather than subtle:

check result
list on the default Regia reads shared
list --account info@… Regia reads own
DWSHELL_ACCOUNT=info@… Regia reads own
list again, no flag shared — the default is untouched
shell shortcut with -c, flag before and after the agent exit 0 both ways
account default, then no flag the other account is now used
unknown --account error listing the registered accounts
account rm with no terminal and no --yes refused
removing one of two survivor promoted

All of it ran against scratch configurations built from real credentials; the working configuration was never modified, and the scratch copies were deleted afterwards.

gofmt, go vet, go test -race ./... green — 18 new tests in internal/config.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG

ale-rinaldi and others added 3 commits September 5, 2026 23:38
Choosing between DWService accounts per command, the way the AWS CLI
chooses a profile, under two constraints that shape the whole design:
someone who does not want the feature must never learn it exists, and an
existing configuration keeps working.

Hence the email as the key, the first account registered becoming the
default silently, and a flat configuration migrated in memory on load
rather than rewritten underneath a read-only command.

@ is not available as a selector: dwshell alice@myserver already means
the remote OS user.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
Six tasks. The design keeps the multi-account logic inside
internal/config, so the client changes from cfg.Session to
cfg.Current().Session and little else, and the two constraints that
motivated the feature are each pinned by a test rather than left as
intentions: loading a flat configuration must not rewrite the file, and
a single account must be usable whether or not it is marked default.

The live check exploits a property of the two real accounts: the second
owns the machines the first sees as shares, so a wrong selection shows
up as own-versus-shared on the same machine instead of something subtle.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
Log in twice with different emails and both are kept; choose between
them with --account or DWSHELL_ACCOUNT. The email is the account's
identity, so logging in again with a known one refreshes it, exactly as
before.

The feature is built to be invisible until it is wanted. With a single
account there is no default to think about: Select uses the lone account
whether or not it is marked default, no output changes, and the word
appears only in `account list`. That is a test, not an intention.

An existing configuration is migrated in memory on load and written in
the new shape only when something saves for a reason of its own — a
read-only command must not rewrite the user's file behind their back.
Verified against a real configuration: after `list` the file still had
its flat keys, and only a command that saves turned them into accounts.

Removing the default promotes the survivor when exactly one remains and
otherwise leaves it unset, refusing later commands with an explanation
rather than silently pointing them at the wrong account.

login refuses --account: which account it touches is decided by the
email being logged in, and it deliberately does not select an account,
since "several accounts and no default" is a state logging in resolves.

Verified live with two real accounts, which see the same machines from
opposite sides: Regia reads shared from one and own from the other, so a
wrong selection would be obvious rather than subtle. Checked through the
flag, the environment variable, the default, and back.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
@ale-rinaldi
ale-rinaldi merged commit f04dde1 into main Sep 5, 2026
1 check passed
@ale-rinaldi
ale-rinaldi deleted the feat/multi-account branch September 5, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant